﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AlgorithmResult;
using System.Diagnostics;
using SimilarityToAncientCodon;

namespace CodonSegDP
{

  


	public class DPAlg
	{
       
        private int codonSize = 3;
        public Boolean NeedToStop {get;set;}
      
        public event ProgressEventHandler ProgressHandler = null;
        public String Text = String.Empty;
        int minSegmentSize = 2;
        Segment leftSegment;
        public Segment LeftSegment
        {
            get
            {
                return leftSegment;
            }
            set
            {
                if (leftSegment == value)
                    return;
                leftSegment = value;
            }
        }
        Segment rightSegment;
        public Segment RightSegment
        {
            get
            {
                return rightSegment;
            }
            set
            {
                if (rightSegment == value)
                    return;
                rightSegment = value;
            }
        }
        public DPAlg(String _Text, int _minSegmentSize, int _codonSize = 3)
        {
            codonSize = _codonSize;
            Text = _Text;
            minSegmentSize = _minSegmentSize;
            leftSegment = new Segment(Text, codonSize, minSegmentSize);
            rightSegment = new Segment(Text, codonSize, minSegmentSize);
            NeedToStop = false;
        }
        public Result TripletBounderiesSegmentator()

        {
          int  curPostion=0;
         
           
          double[] costSegment = new double[Text.Length];
          String[] tmpArchreTriplets = new String[Text.Length];
          int i=0;
          long[] BoundariesSet = new long[Text.Length / minSegmentSize - 1];
          String[] ArchreTriplets = new String[Text.Length / minSegmentSize - 1];
            
          bool needNewIteration=false;
          int boundery_index = 0, costSegmentIndx=0;
          int lastMaxCostSegment = 0;
          int lastBoundaryValueIndex = 0;
        
          int maxCostSegment = 0;
          int numOftripletsforLeftSide=minSegmentSize;
          int numOftripletsforRightSide=minSegmentSize;
          int windowSize = (minSegmentSize * 2) * codonSize;
          Dictionary<double, int> dict = new Dictionary<double, int>();

     
          while (curPostion != Text.Length && (curPostion + windowSize) < Text.Length && !NeedToStop)
            {
               
                    needNewIteration=false;
                    i = 0;


                  
                    curPostion = leftSegment.AddTriplets(curPostion, numOftripletsforLeftSide); //Add minsegSize for left matrix
                    rightSegment.AddTriplets(curPostion , numOftripletsforRightSide);//Add minsegSize for right matrix

                    costSegment[costSegmentIndx] = leftSegment.EvaluateCostFunction() + rightSegment.EvaluateCostFunction(); //eval costFunc (left+right)
                    tmpArchreTriplets[costSegmentIndx] = leftSegment.ArcheCodonName;    //add temp ArcheCodon Name to tmpArray
                    dict[costSegment[costSegmentIndx]] = curPostion;            //store the position costFunc -> position
                    costSegmentIndx++;
                    
                  do{
                  
                      i = 1;
                      while (i < minSegmentSize && (curPostion + windowSize) < Text.Length)
                        {                             
                            leftSegment.AddTripletToSegment(Text.Substring(curPostion, codonSize)); // add one triplet from right segment
                            rightSegment.RemoveTripletFromSegment(Text.Substring(curPostion, codonSize));//remove first triplet from right segment

                            rightSegment.AddTripletToSegment(Text.Substring(curPostion + minSegmentSize * codonSize, codonSize));  //add one triplet from next
                            curPostion += codonSize;
                            
                            i++;
                            costSegment[costSegmentIndx] = leftSegment.EvaluateCostFunction() + rightSegment.EvaluateCostFunction(); //eval costFunc (left+right)
                            tmpArchreTriplets[costSegmentIndx] = leftSegment.ArcheCodonName;    //add temp ArcheCodon Name to tmpArray
                            dict[costSegment[costSegmentIndx]] = curPostion;            //store the position costFunc -> position
                            costSegmentIndx++;
                        }
                      maxCostSegment = FindMaxCostSegment(costSegment, ref costSegmentIndx);
                    
                      if (maxCostSegment != lastMaxCostSegment)
                        { //if we find boundary better than first one

                            lastMaxCostSegment = maxCostSegment;
                            curPostion = dict[costSegment[lastMaxCostSegment]];
                            needNewIteration = true;
                            
                            i = 0;
                        }
                        else {
                            //Stopwatch sw = new Stopwatch();
                            //sw.Start();
                            //if (curPostion > dict[costSegment[maxCostSegment]])
                            //    Console.WriteLine("Come Back {0} stpes", curPostion - dict[costSegment[maxCostSegment]]);
                            curPostion = dict[costSegment[maxCostSegment]];
                            lastBoundaryValueIndex = curPostion;
                            ArchreTriplets[boundery_index] = tmpArchreTriplets[maxCostSegment];
                            if ((boundery_index > 0 && ArchreTriplets[boundery_index - 1] == ArchreTriplets[boundery_index]) == false)
                            {

                                BoundariesSet[boundery_index++] = curPostion;
                                if (ProgressHandler != null)
                                {

                                    ProgressHandler(this, new progressEvent(curPostion, null));
                                }
                            }
                            else {
                                ArchreTriplets[boundery_index] = String.Empty;
                                BoundariesSet[boundery_index-1] = curPostion;
                            }
                          

                            needNewIteration = false;
                            lastMaxCostSegment = 0;
                           
                           leftSegment.ZeroSegNuclDistribution();
                           rightSegment.ZeroSegNuclDistribution();
                           dict = new Dictionary<double, int>(); 
                           ZeroArcheTripletsStrings(tmpArchreTriplets, ref costSegmentIndx);
                           ZeroCosegment(costSegment, ref costSegmentIndx);
                           costSegmentIndx = 0;
                           //sw.Stop();
                           //if (sw.ElapsedMilliseconds!=0)
                           //Console.WriteLine(" Time: {0} ms", sw.ElapsedMilliseconds);
                          
                        }
                }while(needNewIteration);
             
             
          
          }


        
          while (curPostion + codonSize <= Text.Length)
          {
              leftSegment.AddTripletToSegment( Text.Substring(curPostion, codonSize));
              curPostion += codonSize;


          }
          String ArcheTriplet = String.Empty;
          leftSegment.EvaluateCostFunction();
          ArcheTriplet = leftSegment.ArcheCodonName;
          if (ArcheTriplet == ArchreTriplets[boundery_index - 1])
          {
              BoundariesSet[boundery_index - 1] = curPostion;
          }
          else {
              BoundariesSet[boundery_index] = curPostion;
              ArchreTriplets[boundery_index] = ArcheTriplet;
              boundery_index++;
 
          }
          
          Result bestResultOfSegGen = new Result(BoundariesSet, ArchreTriplets, boundery_index);
          FragmentSimToAncientCodon simToAncientCodon = new FragmentSimToAncientCodon(bestResultOfSegGen, Text, codonSize);
         
          return bestResultOfSegGen;
}

        private void ZeroArcheTripletsStrings(string[] tmpArchreTriplets, ref int costSegmentIndx)
        {
            for (int i = 0; i < costSegmentIndx; i++)
            {
                tmpArchreTriplets[i] = String.Empty ;
            }
        }

        private long FindLastBoundaryIndx(long[] BoundariesSet)

        {
            long index = BoundariesSet.LastOrDefault<long>(num => num > 0);
       //     for(int i=0;i<BoundariesSet.Length)
            return index;
        }

   

        private void ZeroCosegment(double[] costSegment, ref int costSegmentIndx)
        {
            for (int i = 0; i < costSegmentIndx; i++)
            {
                costSegment[i] = 0;
            }
           
        }


        private int FindMaxCostSegment(double[] costsegment, ref int costSegmentIndx)
        {
            int max_cost_index=0;
            double max_value = 0;

            for (int i = 0; i < costSegmentIndx; ++i)
            {
                if (costsegment[i] >= costsegment[max_cost_index])
                {
                    max_cost_index = i;
                }
            }
            return max_cost_index;
 
        }
        



	}
}
